package cn.starremote.component.utils.encrypt;

import org.bouncycastle.crypto.engines.SM4Engine;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.util.encoders.Hex;

import java.io.UnsupportedEncodingException;

/**
 * @Auther: Wang Jianwei
 * @Date: 2019/9/12 10:54
 * @Description: SM4算法
 */
public class SM4Util {
    //加解密的字节快大小
    public static final int BLOCK_SIZE = 16;

    /**
     * SM4ECB加密算法
     * @param in            待加密内容
     * @param keyBytes      密钥
     * @return
     */
    public static byte[] encryptByEcb0(byte[] in, byte[] keyBytes) {
        SM4Engine sm4Engine = new SM4Engine();
        sm4Engine.init(true, new KeyParameter(keyBytes));
        int inLen = in.length;
        byte[] out = new byte[inLen];

        int times = inLen / BLOCK_SIZE;

        for (int i = 0; i < times; i++) {
            sm4Engine.processBlock(in, i * BLOCK_SIZE, out, i * BLOCK_SIZE);
        }

        return out;
    }

    /**
     * SM4ECB加密算法
     * @param in            待加密内容
     * @param keyBytes      密钥
     * @return
     */
    public static String encryptByEcb(byte[] in, byte[] keyBytes) {
        byte[] out = encryptByEcb0(in, keyBytes);
        String cipher = Hex.toHexString(out);
        return cipher;
    }

    /**
     * SM4的ECB加密算法
     * @param content   待加密内容
     * @param key       密钥
     * @return
     */
    public static String encryptByEcb(String content, String key) {
        byte[] in = Hex.decode(content);
        byte[] keyBytes = Hex.decode(key);

        String cipher = encryptByEcb(in, keyBytes);
        return cipher;
    }

    /**
     * SM4的ECB解密算法
     * @param in        密文内容
     * @param keyBytes  密钥
     * @return
     */
    public static byte[] decryptByEcb0(byte[] in, byte[] keyBytes) {
        SM4Engine sm4Engine = new SM4Engine();
        sm4Engine.init(false, new KeyParameter(keyBytes));
        int inLen = in.length;
        byte[] out = new byte[inLen];

        int times = inLen / BLOCK_SIZE;

        for (int i = 0; i < times; i++) {
            sm4Engine.processBlock(in, i * BLOCK_SIZE, out, i * BLOCK_SIZE);
        }

        return out;
    }

    /**
     * SM4的ECB解密算法
     * @param in        密文内容
     * @param keyBytes  密钥
     * @return
     */
    public static String decryptByEcb(byte[] in, byte[] keyBytes) {
        byte[] out = decryptByEcb0(in, keyBytes);
        String plain = Hex.toHexString(out);
        return plain;
    }

    /**
     * SM4的ECB解密算法
     * @param cipher    密文内容
     * @param key       密钥
     * @return
     */
    public static String decryptByEcb(String cipher, String key) {
        byte[] in = Hex.decode(cipher);
        byte[] keyBytes = Hex.decode(key);

        String plain = decryptByEcb(in, keyBytes);
        return plain;
    }


    /**
     * SM4的ECB加密算法(PKCS#7 填充)
     * @param content   待加密内容（普通字符串: 1234567812345678aa1100）
     * @param key       密钥(十六进制字符串 :B6D8C12920418AFEA57514132E16039A)
     * @return     返回十六进制字符串:
     * ebd4745c5cb68922bfb65af306363b42c8793f648dafdc218b6c06158dbeadec
     */
    public static String sm4EncryptByEcb(String content, String key) throws UnsupportedEncodingException {
        byte[] in = content.getBytes("UTF-8");
        String cipher = "";
        byte[] keyBytes = Hex.decode(key);
        // 待加密串字节长度不是16的倍数，则需填充成16的整数倍
        byte[] fillData = DataPadding.encodeWithPKCS5(in,BLOCK_SIZE);
        cipher = encryptByEcb(fillData, keyBytes);

        return cipher;
    }


    /**
     * SM4的ECB解密算法 (PKCS#7 填充)
     * @param cipher    密文内容 (十六进制字符串:
     *                 ebd4745c5cb68922bfb65af306363b42c8793f648dafdc218b6c06158dbeadec)
     * @param key       密钥 (十六进制字符串: B6D8C12920418AFEA57514132E16039A)
     * @return  返回普通字符串: 1234567812345678aa1100
     */
    public static String sm4decryptByEcb(String cipher, String key) {
        byte[] in = Hex.decode(cipher);
        byte[] keyBytes = Hex.decode(key);
        // 解密
        byte[] out = decryptByEcb0(in, keyBytes);
        // 去掉填充字符
        byte[]respMsg = DataPadding.decodeWithPKCS5(out);
        String plain = new String(respMsg);
        return plain;
    }

}
